package studyguide.ch14;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.io.Connector;
import javax.microedition.io.ServerSocketConnection;
import javax.microedition.io.SocketConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class SocketMidlet extends MIDlet {

	private final Command connectCommand;
	private final Command waitConnectionCommand;
	private final Command exitCommand;
	private final Display display;
	
	private final Form mainForm;
	
	private final TextField receivePort;
	private final TextField connectServer;
	private final StringItem status;
	private final TextField communications;
	
	private class SampleCommandListener implements CommandListener {
		public void commandAction(Command command, Displayable displayable) {
			if (command == exitCommand) {
				destroyApp(true);
				notifyDestroyed();
			} else {
				throw new RuntimeException("UNKNOWN COMMAND" + command);
			}
		}
	}
	
	private class SampleItemCommandListener implements ItemCommandListener {

		public void commandAction(Command command, Item item) {
			if (!isInputBlocked()) {
				if (command == connectCommand) {
					Thread thread = new Thread(new NetworkClient());
					status.setText("connecting");
					blockInput();
					thread.start();
				} else if (command == waitConnectionCommand) {
					Thread thread = new Thread(new NetworkServer());
					status.setText("waiting conection");
					blockInput();
					thread.start();
				} else {
					throw new RuntimeException("UNKNOWN COMMAND" + command);
				}
			}
		}
		
	}
	
	private class NetworkServer implements Runnable {
		public void run() {
			SocketConnection conn = null;
			ServerSocketConnection serverConn = null;
			try {
				serverConn = (ServerSocketConnection) 
					Connector.open("socket://:" + receivePort.getString());
				conn = (SocketConnection) serverConn.acceptAndOpen(); // Wait for a connection
				serverConn.close(); // Do not wait for more connections
				
				DataInputStream in = conn.openDataInputStream();
				DataOutputStream out = conn.openDataOutputStream();
				
				out.writeUTF("This message was generated at the server.\n");
				communications.insert(in.readUTF(), communications.size());
				
				in.close();
				out.close();
			} catch (IOException e) {
				status.setText("error:" + e);
			} finally {
				unblockInput();
				if (serverConn != null) {
					try {
						serverConn.close();
					} catch (IOException e) {
						status.setText("error closing connection:" + e.getMessage());
					}
				}
				if (conn != null) {
					try {
						conn.close();
					} catch (IOException e) {
						status.setText("error closing connection:" + e.getMessage());
					}
				}
			}
		}
	}
	
	private class NetworkClient implements Runnable {
		public void run() {
			SocketConnection conn = null;
			try {
				conn = (SocketConnection) 
					Connector.open("socket://" + connectServer.getString());
				
				DataInputStream in = conn.openDataInputStream();
				DataOutputStream out = conn.openDataOutputStream();
				
				communications.insert(in.readUTF(), communications.size());
				out.writeUTF("This message was sent by the client.\n");
				
				in.close();
				out.close();
			} catch (IOException e) {
				status.setText("error:" + e);
			} finally {
				unblockInput();
				if (conn != null) {
					try {
						conn.close();
					} catch (IOException e) {
						status.setText("error closing connection:" + e.getMessage());
					}
				}
			}
		}
	}
	
	public SocketMidlet() throws IOException {
		mainForm = new Form("Socket Sample");
		exitCommand = new Command("Exit", Command.EXIT, 1);
		connectCommand = new Command("Connect", Command.SCREEN, 1);
		waitConnectionCommand = new Command("Wait Connection", Command.SCREEN, 1);
		
		receivePort = new TextField("Receive connections on port:", "1234", 5, TextField.NUMERIC);
		connectServer = new TextField("Connect to server:", "localhost:1234", 50, TextField.ANY);
		status = new StringItem("Status", "stopped");
		communications = new TextField("Communications:", "", 500, TextField.ANY);
		
		connectServer.setDefaultCommand(connectCommand);
		receivePort.setDefaultCommand(waitConnectionCommand);
		mainForm.addCommand(exitCommand);
		
		SampleCommandListener listener = new SampleCommandListener();
		SampleItemCommandListener itemListener = new SampleItemCommandListener();
		mainForm.setCommandListener(listener);
		connectServer.setItemCommandListener(itemListener);
		receivePort.setItemCommandListener(itemListener);
		
		mainForm.append(status);
		mainForm.append(receivePort);
		mainForm.append(connectServer);
		mainForm.append(communications);

		display = Display.getDisplay(this);
	}
	
	protected void destroyApp(boolean unconditional) {
	}

	protected void pauseApp() {
	}

	protected void startApp() throws MIDletStateChangeException {
		display.setCurrent(mainForm);
	}

	private void blockInput() {
		receivePort.setConstraints(TextField.UNEDITABLE);
		connectServer.setConstraints(TextField.UNEDITABLE);
	}
	
	private void unblockInput() {
		receivePort.setConstraints(TextField.NUMERIC);
		connectServer.setConstraints(TextField.ANY);
	}
	
	private boolean isInputBlocked() {
		boolean isBlocked = false;
		
		if (receivePort.getConstraints() == TextField.UNEDITABLE) {
			isBlocked = true;
		} else if (connectServer.getConstraints() == TextField.UNEDITABLE) {
			isBlocked = true;
		}
		
		return isBlocked;
	}
}
